package cn.wolfcode.shiro;

import cn.wolfcode.domain.Employee;
import cn.wolfcode.service.IEmployeeService;
import cn.wolfcode.service.IPermissionService;
import cn.wolfcode.service.IRoleService;
import cn.wolfcode.util.UserContext;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;

/**
 * Created by 甘静斌 on 2020-07-18 11:35
 */


/**
 * 自定义数据源
 *
 */

@Component
public class CrmRealm extends AuthorizingRealm {

    @Autowired
    private IEmployeeService employeeService;


    @Autowired
    private IPermissionService permissionService;

    @Autowired
    private IRoleService roleService;
    /**
     *
     * 提供授权信息
     * @param principals
     * @return
     */
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

        //1.获取当前登录员工对象,获取员工id
        Employee currentUser = UserContext.getCurrentUser();
        Long employeeId = currentUser.getId();
        //2.查询数据库该员工的拥有的角色和权限数据

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        //3.设置当前登录用户的拥有的角色和权限数据
        if (!currentUser.isAdmin()){   //判断是否管理员,如果不是就需要查询

            List<String> expressions = permissionService.selectExpressionByEmpId(employeeId);
            //设置权限
            info.addStringPermissions(expressions);
            //获取角色数据
            List<String> sns =  roleService.selectSnByEmId(employeeId);
            //设置角色数据
            info.addRoles(sns);
        }else {
            //权限拦截的功能是shiro来做的,不知道Employee的属性的意义,它还是按照权限表达式来判断的,可以给通配符
            info.addStringPermission("*:*");
            info.addRole("admin");    //管理员的角色
        }

        return info;
    }


    /**
     *
     * 提供认证信息
     * @param token
     * @return
     * @throws AuthenticationException
     */
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        //当前方法如果直接返回null,shiro会自动抛出账号不存在的异常
        //如果返回的结果不为null,shiro会自动返回的对象中获取到真实的密码,再与token去对比

        //1.获取令牌中的用户名(前端传来的)
        String username = (String) token.getPrincipal();

        //2.查询数据中是否存在该员工(模拟数据库中的数据)
        Employee employee = employeeService.selectAndName(username);
        //3.如果不存在,直接返回null
        if (employee!=null){
            if(!employee.isStatus()){
                throw new DisabledAccountException("该账户已被禁用,请联系管理员");
            }
            //如果直接跑shiro提供的异常,就不会封装,如果抛自定义的异常,就会被shiro封装

            //4.如果存在,返回SimleAuthenticationInfo对象
            //身份信息可以在任意地方获取到,用来跟subject绑定在一起的
            //在项目中时就直接传入员工对象,跟subject绑定在一起,方便我们后续在任意地方获取当前登录员工对象
            //传入真实的密码,shiro会自动判断是否正确
            //传入当前数据源的名字,对于我们现在的项目没有作用,一般是一个项目中有多个数据源时,需要做标志,代表数据是从哪个数据源中查的
           // return new SimpleAuthenticationInfo(employee,employee.getPassword(),this.getName());
            return new SimpleAuthenticationInfo(employee,employee.getPassword(),ByteSource.Util.bytes(username),this.getName());
        }

        return null;
    }
}
